﻿using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using ST.Common.Util.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace ST.Common.Util.Security
{
	/// <summary>
	/// 安全帮助类，提供SHA-1、SHA256、MD5、AES算法等
	/// </summary>
	public class EncryptHelper
	{
		/// <summary>
		/// 采用SHA-1算法加密字符串（小写形式，长度：）
		/// </summary>
		/// <param name="message">需要加密的字符串</param>
		/// <returns></returns>
		public static string SHA1Hash(string message, bool upperCase = false)
		{
			var sha1 = SHA1.Create();
			var sha1Arr = sha1.ComputeHash(Encoding.UTF8.GetBytes(message));
			StringBuilder buf = new StringBuilder();

			if (upperCase)
			{
				foreach (var b in sha1Arr)
				{
					buf.AppendFormat("{0:X2}", b);
				}
			}
			else
			{
				foreach (var b in sha1Arr)
				{
					buf.AppendFormat("{0:x2}", b);
				}
			}

			return buf.ToString();
		}

		/// <summary>
		/// HMAC SHA256 加密
		/// </summary>
		/// <param name="message">加密消息原文</param>
		/// <param name="secret">秘钥</param>
		/// <returns></returns>
		public static string SHA256Hash(string message, string secret, Encoding encoding, bool upperCase = false)
		{
			byte[] bytesKey = encoding.GetBytes(secret);
			byte[] bytesMsg = encoding.GetBytes(message);
			using (var hmacsha256 = new HMACSHA256(bytesKey))
			{
				byte[] hashmessage = hmacsha256.ComputeHash(bytesMsg);
				StringBuilder buf = new StringBuilder();
				if (upperCase)
				{
					foreach (var b in hashmessage)
					{
						buf.AppendFormat("{0:X2}", b);
					}
				}
				else
				{
					foreach (var b in hashmessage)
					{
						buf.AppendFormat("{0:x2}", b);
					}
				}

				return buf.ToString();
			}
		}

		/// <summary>
		/// HMAC SHA256 加密
		/// </summary>
		/// <param name="message">加密消息原文</param>
		/// <param name="secret">秘钥</param>
		/// <returns></returns>
		public static string SHA256Hash(string message, string secret, bool upperCase = false)
		{
			return SHA256Hash(message, secret, Encoding.UTF8, upperCase);
		}

		/// <summary>
		/// MD5加密
		/// </summary>
		/// <param name="encypStr">需要加密的字符串</param>
		/// <returns></returns>
		public static string MD5Hash(string encypStr, bool upperCase = false)
		{
			//MD5CryptoServiceProvider m5 = new MD5CryptoServiceProvider();
			MD5 md5 = MD5.Create();
			byte[] bytesHashed = md5.ComputeHash(Encoding.UTF8.GetBytes(encypStr));

			StringBuilder buf = new StringBuilder();
			if (upperCase)
			{
				foreach (var b in bytesHashed)
				{
					buf.AppendFormat("{0:X2}", b);//大写：X2，小写：x2
				}
			}
			else
			{
				foreach (var b in bytesHashed)
				{
					buf.AppendFormat("{0:x2}", b);//大写：X2，小写：x2
				}
			}

			return buf.ToString();

			//return BitConverter.ToString(bytesHashed).Replace("-", "");//大写形式
		}

		#region AES加解密
		/// <summary>
		/// AES加密（小写形式）
		/// </summary>
		/// <param name="bytesInput">需要加密的字节数组</param>
		/// <param name="bytesIV">向量</param>
		/// <param name="bytesKey">加密密钥，长度为64位，共8字节</param>
		/// <returns></returns>
		public static byte[] AESEncrypt(byte[] bytesInput, byte[] bytesIV, byte[] bytesKey,
			int keySize = 128, CipherMode mode = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
		{
			Aes aes = Aes.Create();

			aes.Mode = mode;
			aes.KeySize = keySize;
			aes.Padding = padding;
			aes.Key = bytesKey;
			aes.IV = bytesIV;

			using (MemoryStream ms = new MemoryStream())
			{
				using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
				{
					cs.Write(bytesInput, 0, bytesInput.Length);
					cs.FlushFinalBlock();
					byte[] cipherBytes = ms.ToArray();//得到加密后的字节数组   
					return cipherBytes;
				}
			}
		}

		/// <summary>
		/// AES加密（返回base64形式）
		/// </summary>
		/// <param name="inputStr"></param>
		/// <param name="strKey"></param>
		/// <param name="iv">向量</param>
		/// <returns></returns>
		public static string AESEncrypt(string inputStr, string strKey, string iv)
		{
			byte[] bytesInput = Encoding.UTF8.GetBytes(inputStr);
			byte[] bytesIV = Encoding.UTF8.GetBytes(MD5Hash(iv).Substring(0, 16));
			byte[] bytesKey = Encoding.UTF8.GetBytes(MD5Hash(strKey).Substring(0, 16));
			byte[] encryptedData = AESEncrypt(bytesInput, bytesIV, bytesKey);
			return Convert.ToBase64String(encryptedData);
		}

		/// <summary>
		/// AES加密（返回base64形式）
		/// </summary>
		/// <param name="obj"></param>
		/// <param name="strKey"></param>
		/// <param name="iv"></param>
		/// <returns></returns>
		public static string AESEncrypt(object obj, string strKey, string iv)
		{
			return AESEncrypt(JsonConvert.SerializeObject(obj, new JsonSerializerSettings()
			{
				NullValueHandling = NullValueHandling.Ignore
			}), strKey, iv);
		}

		/// <summary>
		/// AES加密（返回base64形式）
		/// </summary>
		/// <param name="inputStr"></param>
		/// <param name="strKey"></param>
		/// <param name="timestamp">时间戳偏移向量</param>
		/// <returns></returns>
		public static string AESEncrypt(string inputStr, string strKey, long timestamp)
		{
			string iv = new DateTime(timestamp).ToString("yyyyMMdd");
			return AESEncrypt(inputStr, strKey, iv);
		}

		/// <summary>
		/// AES加密（返回base64形式）
		/// </summary>
		/// <param name="obj"></param>
		/// <param name="strKey"></param>
		/// <param name="timestamp">时间戳偏移向量</param>
		/// <returns></returns>
		public static string AESEncrypt(object obj, string strKey, long timestamp)
		{
			return AESEncrypt(JsonConvert.SerializeObject(obj, new JsonSerializerSettings()
			{
				NullValueHandling = NullValueHandling.Ignore
			}), strKey, timestamp);
		}

		/// <summary>
		/// AES加密，以当前日期yyyyMMdd为偏移向量（返回base64形式）
		/// </summary>
		/// <param name="inputStr"></param>
		/// <param name="strKey"></param>
		/// <returns></returns>
		public static string AESEncrypt(string inputStr, string strKey)
		{
			return AESEncrypt(inputStr, strKey, DateTime.Now.ToString("yyyyMMdd"));
		}

		/// <summary>
		/// AES加密，以当前日期yyyyMMdd为偏移向量（返回base64形式）
		/// </summary>
		/// <param name="obj"></param>
		/// <param name="strKey"></param>
		/// <returns></returns>
		public static string AESEncrypt(object obj, string strKey)
		{
			return AESEncrypt(JsonConvert.SerializeObject(obj, new JsonSerializerSettings()
			{
				NullValueHandling = NullValueHandling.Ignore
			}), strKey, DateTime.Now.ToString("yyyyMMdd"));
		}

		/// <summary>
		/// AES解密（小写形式）
		/// </summary>
		/// <param name="base64EncryptedStr"></param>
		/// <param name="strKey"></param>
		/// <returns></returns>
		public static string AESDecrypt(string base64EncryptedStr, string strKey, string iv)
		{
			byte[] bytesInput = Convert.FromBase64String(base64EncryptedStr);
			byte[] bytesIV = Encoding.UTF8.GetBytes(MD5Hash(iv).Substring(0, 16));
			byte[] bytesKey = Encoding.UTF8.GetBytes(MD5Hash(strKey).Substring(0, 16));
			byte[] decryptedData = AESDecrypt(bytesInput, bytesKey, bytesIV);
			return Encoding.UTF8.GetString(decryptedData);
		}

		/// <summary>
		/// AES解密（小写形式）
		/// </summary>
		/// <param name="base64EncryptedStr"></param>
		/// <param name="strKey"></param>
		/// <param name="timestamp">时间戳偏移向量</param>
		/// <returns></returns>
		public static string AESDecrypt(string base64EncryptedStr, string strKey, long timestamp)
		{
			string iv = new DateTime(timestamp).ToString("yyyyMMdd");
			return AESDecrypt(base64EncryptedStr, strKey, iv);
		}

		/// <summary>
		/// AES解密，以当前日期yyyyMMdd为偏移向量（小写形式）
		/// </summary>
		/// <param name="base64EncryptedStr"></param>
		/// <param name="strKey"></param>
		/// <returns></returns>
		public static string AESDecrypt(string base64EncryptedStr, string strKey)
		{
			string iv = DateTime.Now.ToString("yyyyMMdd");
			return AESDecrypt(base64EncryptedStr, strKey, iv);
		}
		/// <summary>
		/// AES解密（小写形式）
		/// </summary>
		/// <param name="bytesInput">输入的数据</param>
		/// <param name="bytesKey">key</param>
		/// <param name="bytesIV">向量</param>
		/// <returns></returns>
		public static byte[] AESDecrypt(byte[] bytesInput, byte[] bytesKey, byte[] bytesIV,
			int keySize = 128, CipherMode mode = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
		{
			Aes aes = Aes.Create();

			aes.Mode = mode;
			aes.KeySize = keySize;
			aes.Padding = padding;
			aes.Key = bytesKey;
			aes.IV = bytesIV;

			byte[] decryptBytes = new byte[bytesInput.Length];
			using (MemoryStream ms = new MemoryStream(bytesInput))
			{
				using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Read))
				{
					cs.Read(decryptBytes, 0, decryptBytes.Length);
				}
			}
			return decryptBytes;
		}

		/// <summary>
		/// 微信AES解密（小写形式）
		/// </summary>
		/// <param name="inputdata">输入的数据</param>
		/// <param name="iv">向量</param>
		/// <param name="sessionKey">session_key</param>
		/// <returns></returns>
		public static string WXAESDecrypt(string encryptedData, string iv, string sessionKey)
		{
			byte[] bytesInput = Convert.FromBase64String(encryptedData);
			byte[] bytesIV = Convert.FromBase64String(iv);
			byte[] bytesKey = Convert.FromBase64String(sessionKey);
			byte[] bytesOutput = AESDecrypt(bytesInput, bytesKey, bytesIV);
			string str = Encoding.UTF8.GetString(bytesOutput);

			return str;
		}
		#endregion

		#region 3DES加解密
		public static string TripleDESEncrypt(string input, string key)
		{
			using TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider
			{
				Key = Encoding.UTF8.GetBytes(key),
				Mode = CipherMode.ECB,
				Padding = PaddingMode.PKCS7
			};
			using ICryptoTransform transform = des.CreateEncryptor();
			byte[] bytesInput = Encoding.UTF8.GetBytes(input);
			var bytesCrypt = transform.TransformFinalBlock(bytesInput, 0, bytesInput.Length);
			return Convert.ToBase64String(bytesCrypt);
		}

		public static string TripleDESDecrypt(string strInputBase64, string key)
		{
			try
			{
				using TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider
				{
					Mode = CipherMode.ECB,
					Padding = PaddingMode.PKCS7,
					Key = Encoding.UTF8.GetBytes(key)
				};
				using ICryptoTransform tranform = des.CreateDecryptor();
				var bytesInput = Convert.FromBase64String(strInputBase64);
				var bytesCrypt = tranform.TransformFinalBlock(bytesInput, 0, bytesInput.Length);
				return Encoding.UTF8.GetString(bytesCrypt);
			}
			catch (Exception ex)
			{
				LoggingHelper.CreateLogger<EncryptHelper>().LogError(ex, "3DES解密失败");
				return string.Empty;
			}
		}
		#endregion
	}
}
